 #ifdef __WXMSW__
  #define _CRT_SECURE_NO_WARNINGS
  #define _WIN32_WINNT 0x501
  #include <winsock2.h>
  #include <Ws2tcpip.h>
 #endif

 #ifdef _MSC_VER
  #pragma warning(disable:4127) 
 #endif 
 
 #ifndef __WXMSW__
  typedef int SOCKET;
  #define INVALID_SOCKET -1
  #define SOCKET_ERROR   -1
 #endif

 #include <common.h>
 #include <util.h>

 SOCKET  sdClient, sdServer;
 u_long  argp;

 void
  state_machine(connection *);

 void
  process_ftp(connection *);

 bool
  create_server_socket(const char *, u_short);

 void
  check_incoming_requests(connection *);

 int
  host_connect(const char *, char *, SOCKET *, bool);

 void
  process_ftp (connection *);

 FD_SET  *prFDs;
 FD_SET  *pwFDs;

 u_short ipcport = 1234;

 #ifdef __WXMSW__
  unsigned int __stdcall
 #else
  void *
 #endif

 session_thread (void * arg)
 {
   logit_a ("%s(%d): ->\n", __func__, GetCurrentThreadId());

   connection *c = (connection *)arg;
   int i = 0, r;

   #ifndef __WXMSW__
    int nfds
   #endif

   /*
    *  create a server socket over which UI
    *  would send connection queue requests 
    *  right now it tries three times starting 
    *  from "ipcport" todo: put this in a loop 
    *  till we find an unused port..
    *
    */

   if (!create_server_socket("127.0.0.1", ipcport)) {
     if (!create_server_socket("127.0.0.1", ++ipcport)) {
       if (!create_server_socket("127.0.0.1", ++ipcport)) {
        #ifdef __WXMSW__
          return (1);
        #else
          return (arg);
        #endif
       }
     }
   }

   #ifndef __WXMSW__
    nfds = sdServer + 1;
   #endif

   for (;;) {

    r = i = 0;

    prFDs = new FD_SET;

    FD_ZERO (prFDs);

    while (i < MAX_SESSIONS) {

     if (c[i].m_state == 0) {
       logit_a ("before select c[%d].m_state = 0\n", i);
       i++;
       continue;
     }    
    
     if (c[i].m_state == STATE_NEW) {

       logit_a ("%s: setting connect request for %s %s at index %d\n", __func__ , c[i].m_host, c[i].m_port, i);

      /*
       * This is required because sometimes a connect request does not complete in one iteration.
       * The socket however is created in the first iteration. Hence if we have a connection object 
       * with state_new and a socket, this means that the connection request did not yeild the
       * first time select was called on it for writability. In such cases do not initiate another 
       * connect, instead just place the socket in the pwFDs.
       */

       if (!c[i].m_sd) {
         host_connect(c[i].m_host, c[i].m_port, &(c[i].m_sd), true);
       }

       if (c[i].m_state) {

         if (!pwFDs) {
           pwFDs = new FD_SET;
           FD_ZERO (pwFDs);
         }

         FD_SET (c[i].m_sd, pwFDs);

         #ifndef __WXMSW__
          if (c[i].m_sd > nfds)
            nfds = c[i].m_sd + 1;
         #endif
       }

     } else {

       logit_a ("%s: setting read request for -> %s at index %d\n", __func__ , c[i].m_host, i);

       FD_SET (c[i].m_sd, prFDs);

       #ifndef __WXMSW__
        if (c[i].m_sd > nfds)
          nfds = c[i].m_sd + 1;
       #endif
     }

    i++;
   }

   FD_SET (sdServer, prFDs);

   logit_a ("%s: select ->\n",  __func__);

   #ifndef __WXMSW__
   
     r = select (nfds, prFDs, pwFDs, NULL, NULL);

   #else

     timeval tv = {0,0};

     tv.tv_sec = 5;

     r = select (0, prFDs, pwFDs, NULL, pwFDs ? &tv :NULL);

   #endif

   logit_a ("%s: select <- %d prFDs %x pwFDs %x\n", __func__ , r, prFDs, pwFDs);

   for (i = 0; i < MAX_SESSIONS; i++) {

     if (c[i].m_state == 0) {
       logit_a ("after select c[%d].m_state = 0\n", i);
       continue;
     }
   
     if (pwFDs && FD_ISSET(c[i].m_sd, pwFDs)) {  

        /* connection successful */

        logit_a ("%s: connection object at index %d is writable\n", __func__ , i);

        #ifdef __WXMSW__
           ioctlsocket (c[i].m_sd, FIONBIO, &argp);
        #else
           fcntl (c[i].m_sd, F_SETFL, O_NONBLOCK);
        #endif

        if (c[i].isPrimary) {
          logit_a ("Connected to %s\n", c[i].m_host);
        }

        if (c[i].m_protocol == PROTO_FTP_CC ||
            c[i].m_protocol == PROTO_FTP_DC) { /* ftp */

	    if (c[i].m_protocol == PROTO_FTP_CC) {

              c[i].m_state = 1;

              if (!c[i].isPrimary) { /* cc but not primary */

                //c[i].m_parent->m_next = &c[i];
              }
            } else {

              c[i].m_state = STATE_DC;
            }

            process_ftp(&c[i]);

        } else if (c[i].m_protocol == PROTO_XMPP) {

           c[i].m_state = 1;

           state_machine(&c[i]);
        }

     } else if (FD_ISSET(c[i].m_sd, prFDs)) {

       /* c[i] is ready to read segregate based on the protocol */

       logit_a ("\n%s: connection object at index %d is readable\n", __func__ , i);

       if (c[i].m_protocol == PROTO_FTP_CC ||
           c[i].m_protocol == PROTO_FTP_DC ) {

          process_ftp(&c[i]);
		  
       } else if(c[i].m_protocol == PROTO_XMPP) {

          state_machine(&c[i]);
       }
     }
    }

    check_incoming_requests(c);

    if (prFDs) {
      FD_ZERO (prFDs);
      delete prFDs, prFDs = 0;
    }

    if (pwFDs) {
      FD_ZERO (pwFDs);
      delete pwFDs, pwFDs = 0;
    }

    if (0) break;

  } /* for(;;) */

  logit_a ("%s: exiting\n",  __func__ );

#ifdef __WXMSW__
  return (1);
#else
  return (arg);
#endif
}

 int
  host_connect(const char *host, char *port, SOCKET *s, bool nonblocking)
 {
   struct addrinfo *ptr = 0;
   struct addrinfo *res = 0;
   int r = -1;

   if (!getaddrinfo(host, port, NULL, &res)) {

     for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {

        if (ptr->ai_family == AF_INET || ptr->ai_family == 0)
         if (ptr->ai_socktype == SOCK_STREAM  || ptr->ai_socktype == 0)
          if (ptr->ai_protocol == IPPROTO_TCP || ptr->ai_protocol == 0) {

            *s = socket (ptr->ai_family, SOCK_STREAM, IPPROTO_TCP);

            if (nonblocking) {

             #ifdef __WXMSW__
               argp = 1;
               ioctlsocket (*s, FIONBIO, &argp);
               argp = 0;
             #else
               fcntl (*s, F_SETFL, O_NONBLOCK);
             #endif

            }

            r = connect (*s, ptr->ai_addr, ptr->ai_addrlen);

            break;
         }
     }

     if (res) freeaddrinfo(res);
   }

    return (r);
 }

 bool
  create_server_socket(const char *ip, u_short port)
 {
   SOCKET listenSocket;

   listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   sockaddr_in service, client_addr;

   service.sin_family      = AF_INET;
   service.sin_addr.s_addr = inet_addr(ip);
   service.sin_port        = htons(port);

   if (bind(listenSocket, (struct sockaddr *) &service, sizeof(service)) == -1) {

     #ifdef __WXMSW__
      closesocket(listenSocket);
     #else
      close(listenSocket);
     #endif

    return (false);
   }

   listen(listenSocket, 5);

   socklen_t sin_size = sizeof (struct sockaddr_in);

   sdServer = accept(listenSocket, (struct sockaddr *)&client_addr,&sin_size);

   if (sdServer == INVALID_SOCKET) {
     #ifdef __WXMSW__
      closesocket(listenSocket);
     #else
      close(listenSocket);
     #endif
    return (false);
   }

   #ifdef __WXMSW__
    closesocket(listenSocket);
   #else
    close(listenSocket);
   #endif

   return (true);
 }

/*
 * This sends connection structure as raw data to the session thread over 
 * the loopback interface and would cause the blocking select call in the 
 * session thread to yield. Session thread would then proceed to update the 
 * clist array
 */

 void
  queue_connection(connection *c)
 {
   if (!sdClient) {

     char port[1024] = {0};

     sprintf(port,"%d",ipcport);

     host_connect("127.0.0.1", port, &sdClient, false);
   }

   logit_a("%s: host [%s] port [%s]\n", __func__, c->m_host, c->m_port);
   
   int r = send(sdClient, (char *)c, sizeof(connection), 0);

   logit_a ("%s: send %d\n", __func__ , r);
 }

 void
  check_incoming_requests(connection *c)
 {
   /* check readability on server socket which indicates new connection requests from the UI */

   if (FD_ISSET(sdServer, prFDs)) {

     connection cNew;

     recv(sdServer, (char *)(&cNew), sizeof(connection), 0);

     int nfree = 0;

     while (c[nfree].m_state) {
       nfree++;
     }

     if (nfree < MAX_SESSIONS) {

       memmove(&(c[nfree]), &cNew, sizeof(connection));

       c[nfree].m_state = STATE_NEW;

       logit_a ("%s: new connection request inserted at %d\n", __func__ , nfree);
     }

   } else {

    logit_a ("%s: no new connection to process\n", __func__);
   }
 }

 int 
  connection::send_data(char *data, int len)
 { 
   int r = 0, i = 0;
 
   if ((this->m_sec).flags & FLAG_SECURE) { 

     while (len > 16*1024) {

      r = SSL_write((this->m_sec).ssl, data + i, 16*1024);
      
      assert(r>0);
      
      i += 16*1024;
      len -= r;
     }

     if (len) {
       r = SSL_write((this->m_sec).ssl, data, len);
     }
     
     if (r <= 0) {
       //print_ssl_error();
     }
     
   } else {

     r = send(this->m_sd, data, len, 0);
   }
  
   return (r);
 }

 int 
  connection::recv_data(char *data, int len, int flags)
 {
   int r = 0;

   if ((this->m_sec).flags & FLAG_SECURE) {

     r = SSL_read((this->m_sec).ssl, data, len);

   } else {

     r = recv(this->m_sd, data, len, flags); 

   }

   return (r); 
 }

 /*
        switch (SSL_get_error((this->m_sec).ssl, r)) {
        case SSL_ERROR_ZERO_RETURN:
        logit_a("SSL_ERROR_ZERO_RETURN");
        break;
        case SSL_ERROR_WANT_READ:
        logit_a("SSL_ERROR_WANT_READ");
        break;
        case SSL_ERROR_WANT_WRITE:
        logit_a("SSL_ERROR_WANT_WRITE");
        break;
        case SSL_ERROR_WANT_CONNECT:
        logit_a("SSL_ERROR_WANT_CONNECT");
        break;
        case SSL_ERROR_WANT_ACCEPT:
        logit_a("SSL_ERROR_WANT_ACCEPT");
        break;
        case SSL_ERROR_WANT_X509_LOOKUP:
        logit_a("SSL_ERROR_WANT_X509_LOOKUP");
        break;
        case SSL_ERROR_SYSCALL:
        logit_a("SSL_ERROR_SYSCALL");
        break;
        case SSL_ERROR_SSL:
        logit_a("SSL_ERROR_SSL");
        break;
        default:
        logit_a("default");
        break;
       }
 
 */